﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com) & Quincy.

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using OF.DB.DataAccess;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OF.DB.DataAccess
{
    public static class DBGenerator
    {
        private const string module = @"using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OF.DB.DataAccess;
using {11};

namespace {9}
{{    
    public static partial class {0}Dao
    {{
        private const string AddSql = @""{1}"";
        private const string UpdateSql = @""{2}"";
        private const string GetByIdSql = @""{3}"";
        private const string DeleteByIdSql = @""{4}"";
        private const string GetALLListSql = @""{10}"";
		
        private static SqlParameter[] GetAddSqlParams({0} entity)
        {{
            return new SqlParameter[] {{
                {5}
            }};
        }}

        private static SqlParameter[] GetUpdateSqlParams({0} entity)
        {{
            return new SqlParameter[] {{
                {6}
            }};
        }}

        private static SqlParameter[] GetGetByIdSqlParams({0} entity)
        {{
            return new SqlParameter[] {{
                {7}
            }};
        }}

        private static SqlParameter[] GetDeleteByIdSqlParams({0} entity)
        {{
            return new SqlParameter[] {{
                {8}
            }};
        }}
    }}
}}";
        private const string moduleDefault = @"using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OF.DB.DataAccess;
using {12};

namespace {9}
{{    
    public static partial class {0}Dao
    {{
        public static int Add({0} entity)
        {{
            //createdate
            object result = DBHelper.ExecuteScale(AddSql, GetAddSqlParams(entity));
            return Convert.ToInt32(result);
        }}

        public static void Update({0} entity)
        {{
            //editdate
            DBHelper.ExecuteNonQuery(UpdateSql, GetUpdateSqlParams(entity));
        }}

        public static {0} GetByKey({10} {11})
        {{
            return DBHelper.ExecuteReader<{0}>(GetByIdSql, GetGetByIdSqlParams(new {0}
            {{
                {11} = {11}
            }})).FirstOrDefault();
        }}

        public static void DeleteByKey({10} {11})
        {{
            DBHelper.ExecuteNonQuery(DeleteByIdSql, GetDeleteByIdSqlParams(new {0}
            {{
                {11} = {11}
            }}));
        }}	
    }}
}}";
        private const string SqlParamTab = ",\r\n               ";
       
        public static void GenerateQuery()
        { 
            
        }
        
        public static string InitOutputPath(string path)
        {
            string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }
            return filePath;
        }

        public static void GenerateBasic(DBGenerateConfig config)
        {
            string tableName = config.TableName;
            //string tableName = string.Format("[{0}]", config.TableName);
            List<DBColumnMeta> dbMetaList = DBHelper.GetTableMeta(tableName);
            string pkColumn = DBHelper.GetPKColumn(tableName);
            string filePath = InitOutputPath(string.Format("{0}Dao.generate.cs", tableName));
            string safeTableName = DBHelper.GetSafeName(tableName);
            GenerateSqlParam addSqlParam = GenerateAdd(safeTableName, config.ignoreInsertColumns, pkColumn, dbMetaList);
            GenerateSqlParam updateSqlParam = GenerateUpdate(safeTableName, config.ignoreUpdateColumns, pkColumn, dbMetaList);
            GenerateSqlParam getByIdSqlParam = GenerateGet(safeTableName, pkColumn, dbMetaList);
            GenerateSqlParam deleteByIdSqlParam = GenerateDelete(safeTableName, pkColumn, dbMetaList);
            string getAllSql = GenerateGetAllList(safeTableName, pkColumn, dbMetaList, config.ignoreSelectAllColumns);
            string content = string.Format(module, tableName, 
                    addSqlParam.Sql, updateSqlParam.Sql, getByIdSqlParam.Sql, deleteByIdSqlParam.Sql,
                    addSqlParam.SqlParam, updateSqlParam.SqlParam, getByIdSqlParam.SqlParam, deleteByIdSqlParam.SqlParam,
                    config.NameSpace, getAllSql, DBHelper.DefaultEntityNameSpace);
            Output(filePath, content);

            filePath = InitOutputPath(string.Format("{0}Dao.cs", tableName));
            var keyDefine = dbMetaList.First(item => item.ColumnName.Equals(pkColumn, StringComparison.InvariantCultureIgnoreCase));
            Tuple<string, bool> typeTuple = DBHelper.GetDonetFieldBySqlType(keyDefine.DBTypeName);
            string keyDonetType = typeTuple.Item1;
            content = string.Format(moduleDefault, tableName,
                    addSqlParam.Sql, updateSqlParam.Sql, getByIdSqlParam.Sql, deleteByIdSqlParam.Sql,
                    addSqlParam.SqlParam, updateSqlParam.SqlParam, getByIdSqlParam.SqlParam, deleteByIdSqlParam.SqlParam,
                    config.NameSpace, keyDonetType, pkColumn, DBHelper.DefaultEntityNameSpace);
            Output(filePath, content);
        }

        public static void Output(string filePath, string content)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }
            }
            catch
            { 
                
            }
            using (FileStream stream = new FileStream(filePath, FileMode.OpenOrCreate))
            {
                stream.Seek(0, SeekOrigin.End);
                byte[] btArr = System.Text.Encoding.UTF8.GetBytes(content);
                stream.Write(btArr, 0, btArr.Length);
            }
        }

        public static GenerateSqlParam GenerateAdd(string tableName, List<string> ignoreInsertColumns, string pkColumn, List<DBColumnMeta> list)
        {
            if (ignoreInsertColumns != null)
            {
                list = list.Where(item => !ignoreInsertColumns.Contains(item.ColumnName)).ToList();
            }
            var identityColumn = list.FirstOrDefault(item => item.IsIdentity);
            if (identityColumn != null)
            {
                list = list.Where(item => !item.ColumnName.Equals(identityColumn.ColumnName)).ToList();
            }
            string insertColumns = string.Join(", ", list.Select(item => item.ColumnName));
            string insertValues = string.Join(", ", list.Select(item => "@" + item.ColumnName));
            string selectIdentitySql = string.Empty;
            if (identityColumn != null)
            {
                selectIdentitySql = string.Format("SELECT {0} = SCOPE_IDENTITY();", identityColumn.ColumnName);
            }
            string sql = string.Format(@"INSERT INTO dbo.{0}({1}) VALUES({2});
                {3}", tableName, insertColumns, insertValues, selectIdentitySql);
            string sqlParam = string.Join(SqlParamTab, list.Select(item => item.SqlParamDefine));
            return new GenerateSqlParam{ Sql = sql, SqlParam = sqlParam };
        }

        public static GenerateSqlParam GenerateUpdate(string tableName, List<string> ignoreUpdateColumns, string pkColumn, List<DBColumnMeta> list)
        {
            if (ignoreUpdateColumns != null)
            {
                list = list.Where(item => !ignoreUpdateColumns.Contains(item.ColumnName)).ToList();
            }

            List<DBColumnMeta> oldList = list;
            if (pkColumn != null)
            {
                list = list.Where(item => !item.ColumnName.Equals(pkColumn)).ToList();
            }
            else
            {
                throw new NotImplementedException();
            } 

            string updateValues = string.Join(", ", list.Select(item => item.ColumnName + " = @" + item.ColumnName));
            string sql = string.Format(@"UPDATE dbo.{0} SET {1} WHERE {2} = @{2};", tableName, updateValues, pkColumn);
            string sqlParam = string.Join(SqlParamTab, oldList.Select(item => item.SqlParamDefine));
            return new GenerateSqlParam { Sql = sql, SqlParam = sqlParam };
        }

        public static string GenerateGetAllList(string tableName, string pkColumn, List<DBColumnMeta> list, List<string> ignoreList)
        {
            string column = "*";
            if (ignoreList != null && ignoreList.Count > 0)
            { 
                column = string.Join(",", list.Select(item => item.ColumnName).Except(ignoreList, new IgnoreCaseComparer()));
            }
            string sql = string.Format(@"SELECT {1} FROM dbo.{0} WITH(NOLOCK)", tableName, column);
            return sql;
        }

        public static GenerateSqlParam GenerateDelete(string tableName, string pkColumn, List<DBColumnMeta> list)
        {
            string sql = string.Format(@"DELETE FROM dbo.{0} WHERE {1} = @{1};", tableName, pkColumn);
            string sqlParam = string.Join(SqlParamTab, list.Where(item => item.ColumnName.Equals(pkColumn)).Select(item => item.SqlParamDefine));
            return new GenerateSqlParam { Sql = sql, SqlParam = sqlParam };
        }

        public static GenerateSqlParam GenerateGet(string tableName, string pkColumn, List<DBColumnMeta> list)
        {
            string sql = string.Format(@"SELECT * FROM dbo.{0} WITH(NOLOCK) WHERE {1} = @{1}", tableName, pkColumn);
            string sqlParam = string.Join(SqlParamTab, list.Where(item => item.ColumnName.Equals(pkColumn)).Select(item => item.SqlParamDefine));
            return new GenerateSqlParam { Sql = sql, SqlParam = sqlParam };
        }

        public class GenerateSqlParam
        {
            public string Sql;
            public string SqlParam;
        }
    }
}
